<template>
  <div class="ai-chat-container">
    <!-- 聊天历史区域 -->
    <div class="chat-history">
      <div v-for="(message, index) in chatHistory" :key="index"
        :class="['message-item', message.role === 'user' ? 'user-message' : 'ai-message']">
        <!-- 用户消息显示在右侧，AI消息显示在左侧 -->
        <template v-if="message.role === 'user'">
          <div class="message-content">
            <div class="message-role">{{ message.role === 'user' ? '落地的枫叶' : 'AI助手' }}</div>
            <div class="message-text" v-html="formatMessage(message.content)"></div>
          </div>
          <div class="message-avatar user-avatar">
            <img :src="userAvatar" alt="用户头像">
          </div>
        </template>
        <template v-else>
          <div class="message-avatar ai-avatar">
            <img :src="aiAvatar" alt="AI头像">
          </div>
          <div class="message-content">
            <div class="message-role">
              <span>AI助手</span>
              <button class="copy-button" @click="copyMessage(message.content)" title="复制内容" v-if="!message.isThinking">
                <i class="el-icon-document-copy"></i>
              </button>
            </div>
            <div :class="['message-text', { 'thinking-message': message.isThinking }]"
              v-html="formatMessage(message.content)"></div>
          </div>
        </template>
      </div>

      <!-- AI思考中的动画 -->
      <div v-if="isThinking" class="message-item ai-message thinking-animation">
        <div class="message-avatar ai-avatar">
          <img :src="aiAvatar" alt="avatar">
        </div>
        <div class="message-content">
          <div class="message-role">AI助手</div>
          <div class="message-text thinking-dots">
            <span></span>
            <span></span>
            <span></span>
          </div>
        </div>
      </div>

      <div ref="chatEnd"></div>
    </div>

    <!-- 输入区域 -->
    <div class="chat-input-area">
      <div class="input-container">
        <textarea v-model="userInput" placeholder="请输入您的问题..." @keydown.enter.prevent="sendMessage"
          class="chat-textarea" ref="inputArea"></textarea>
        <div class="input-actions">
          <button class="action-button" @click="isTyping ? stopTyping() : sendMessage()"
            :disabled="isProcessing || (!userInput.trim() && !isTyping)" :class="{ 'thinking': isTyping }">
            <img :src="isTyping ? stopIcon : sendIcon" :alt="isTyping ? '停止' : '发送'" class="action-icon">
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
// import axios from 'axios';
// import OpenAI from 'openai';  
import MarkdownIt from "markdown-it";
import hljs from "highlight.js";
import "highlight.js/styles/github.css"; // 选择代码高亮主题
export default {
  name: 'HomeView',
  mixins: [],
  components: {},
  props: {},
  data() {
    return {
      userInput: '',
      isProcessing: false,
      isThinking: false, // AI思考状态
      typingSpeed: 20, // 打字速度(ms)
      userAvatar: require('@/assets/people.png'),
      aiAvatar: require('@/assets/ai.png'),
      chatHistory: [
        {
          role: 'ai',
          content: '您好！我是您的AI助手，有什么可以帮助您的吗？'
        }
      ],
      isTyping: false, // 控制打字机效果的状态
      typewriterTimer: null, // 打字机定时器
      currentTypingInterval: null, // 当前打字机定时器，
      sendIcon: require('@/assets/send.svg'),
      stopIcon: require('@/assets/stop.svg'),
    }
  },
  computed: {},
  watch: {
    chatHistory: {
      handler() {
        this.$nextTick(() => {
          this.scrollToBottom();
        });
      },
      deep: true
    }
  },
  created() { },
  mounted() {
    this.scrollToBottom();
  },
  methods: {

    // 模拟打字机效果，增加对思考过程的支持
    simulateTypingResponse(fullText, isThinking = false) {
      this.isThinking = false;
      this.isTyping = true;

      const aiResponseIndex = this.chatHistory.push({
        role: 'ai',
        content: '',
        isThinking: isThinking,
      }) - 1;

      let currentText = '';
      let charIndex = 0;

      const typeNextChar = () => {
        if (charIndex < fullText.length) {
          currentText += fullText.charAt(charIndex);
          this.chatHistory[aiResponseIndex].content = currentText;
          charIndex++;
          this.currentTypingInterval = setTimeout(typeNextChar, this.typingSpeed);
        } else {
          this.isTyping = false;
          this.currentTypingInterval = null;
        }
      };

      typeNextChar();
      return aiResponseIndex;  // 返回消息索引
    },

    // ... existing code ...

    // 处理不同格式的模型响应
    processModelResponse(response) {
      // 如果响应为空，返回错误信息
      if (!response) {
        return '抱歉，未收到有效的响应。';
      }

      try {
        //  响应已经是字符串
        if (typeof response === 'string') {
          return response;
        }

        //  响应是对象，可能有不同的结构
        if (typeof response === 'object') {
          // OpenAI格式: { choices: [{ message: { content: "..." } }] }
          if (response.choices && response.choices.length > 0) {
            if (response.choices[0].message && response.choices[0].message.content) {
              return response.choices[0].message.content;
            } else if (response.choices[0].text) {
              // 某些API可能使用text字段
              return response.choices[0].text;
            }
          }

          // Anthropic Claude格式: { content: [{ text: "..." }] }
          if (response.content && Array.isArray(response.content)) {
            const textParts = response.content
              .filter(item => item.type === 'text' && item.text)
              .map(item => item.text);
            if (textParts.length > 0) {
              return textParts.join('\n');
            }
          }

          // 简单格式: { text: "..." } 或 { content: "..." } 或 { answer: "..." }
          if (response.text) {
            return response.text;
          } else if (response.content && typeof response.content === 'string') {
            return response.content;
          } else if (response.answer) {
            return response.answer;
          } else if (response.response) {
            return response.response;
          }

          // 如果是数组，连接所有文本
          if (Array.isArray(response)) {
            return response.map(item =>
              typeof item === 'string' ? item :
                (item.text || item.content || JSON.stringify(item))
            ).join('\n');
          }

          // 最后尝试将整个对象转为字符串
          return JSON.stringify(response, null, 2);
        }

        //其他类型，转为字符串
        return String(response);
      } catch (error) {
        console.error('处理模型响应时出错:', error);
        return '抱歉，处理响应时出现了问题。';
      }
    },

    // 增强的消息格式化方法，支持代码块和Markdown
    formatMessage(text) {
      if (!text) return '';

      // 创建 MarkdownIt 实例并配置
      const md = new MarkdownIt({
        html: true,        // 允许 HTML
        breaks: true,      // 允许换行
        linkify: true,     // 自动识别链接
        typographer: true, // 启用一些语言中立的替换和引号
        highlight: (code, lang) => {
          if (lang && hljs.getLanguage(lang)) {
            try {
              return `<div class="code-block language-${lang}"><div class="code-header">${lang}</div><pre><code>${hljs.highlight(code, { language: lang }).value}</code></pre></div>`;
            } catch (e) {
              console.error("高亮处理错误:", e);
            }
          }
          // 如果没有指定语言或语言不支持，使用普通代码块
          return `<div class="code-block"><pre><code>${md.utils.escapeHtml(code)}</code></pre></div>`;
        }
      });

      // 添加表格样式类
      const defaultRender = md.renderer.rules.table_open || function (tokens, idx, options, env, self) {
        return self.renderToken(tokens, idx, options);
      };

      md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
        tokens[idx].attrJoin('class', 'markdown-table');
        return defaultRender(tokens, idx, options, env, self);
      };

      // 使用 MarkdownIt 渲染 Markdown
      const renderedHtml = md.render(text);

      return renderedHtml;
    },

    // 新增：HTML转义函数，防止XSS攻击
    escapeHtml(unsafe) {
      return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
    },

    async sendMessage() {
      const message = this.userInput.trim();
      if (!message || this.isProcessing) return;

      // 添加用户消息到聊天历史
      this.chatHistory.push({
        role: 'user',
        content: message
      });

      this.userInput = '';
      this.isProcessing = true;

      try {
        // 添加思考过程消息
        this.chatHistory.push({
          role: 'ai',
          content: '让我思考一下...',  // 添加初始思考内容
          isThinking: true
        });

        // 调用 API 获取回复
        const aiResponse = await this.callDeepseekAPI(message);
        console.log(aiResponse, '====--------')

        // 添加最终回答（作为新消息）
        this.simulateTypingResponse(aiResponse);
      } catch (error) {
        this.isThinking = false;
        this.chatHistory.push({
          role: 'ai',
          content: '抱歉，服务出现了一些问题，请稍后再试。'
        });
        console.error('发送消息时出错:', error);
      } finally {
        this.isProcessing = false;
      }
    },

    // 修改 callDeepseekAPI 方法中的流处理逻辑
    async callDeepseekAPI(message) {
      try {
        this.isThinking = true;
        const data = {
          model: "/home/llm_deploy/QwQ-32B",
          messages: [
            {
              role: "system",
              content: "你是一个专业的AI助手。"
            },
            {
              role: "user",
              content: message
            }
          ],
          max_tokens: 1000,
          temperature: 0.7,
          stream: true
        };
        // 刚开始使用axios发送请求，但是axios不支持流式响应，所以改用fetch,不知道为啥不生效，百度说因为流式响应的原因，axios不支持，但是fetch支持，但是fetch也不生效，艹

        // 使用 fetch 代替 axios 发送请求
        // 使用 response.body.getReader() 获取可读流
        // 使用 reader.read() 方法逐块读取数据
        // 原生的 Fetch API 和 ReadableStream 来处理流式响应，这在浏览器环境中更合适
        // axios 在浏览器环境中处理流式响应的方式与 Node.js不同， 是返回一个 ReadableStream 对象。

        const response = await fetch('http://10.34.93.9:9900/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer appop'
          },
          body: JSON.stringify(data)
        });

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        console.log(response, '===')

        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let currentThinkingText = '';
        let finalResponse = '';
        let hasThinkTag = false;
        let thinkParts = ['', '']; // 分别存储思考过程和最终答案
        console.log(reader, 'reader++++++++')
        console.log(decoder, 'decoder++++++++')

        return new Promise((resolve, reject) => {
          const processStream = async () => {
            try {
              let isDone = false;
              while (!isDone) {
                const { done, value } = await reader.read();
                if (done) {
                  isDone = true;

                  // 如果有分隔符，返回最终答案部分
                  if (hasThinkTag && thinkParts[1]) {
                    resolve(thinkParts[1].trim());
                  } else {
                    // 否则返回完整响应
                    resolve(finalResponse);
                  }
                  break;
                }

                const chunk = decoder.decode(value, { stream: true });
                const lines = chunk.split('\n').filter(line => line.trim() !== '');

                for (const line of lines) {
                  if (line.startsWith('data: ')) {
                    const jsonData = line.substring(6);
                    if (jsonData === '[DONE]') continue;

                    try {
                      const parsedData = JSON.parse(jsonData);
                      if (parsedData.choices && parsedData.choices.length > 0) {
                        const content = parsedData.choices[0].delta?.content || '';

                        // 累积到完整响应
                        finalResponse += content;

                        //console.log(finalResponse);
                        // finalResponse 返回的内容中 每次都是以 </think>' 结尾来隔断思考过程和最终答案，所以这里用来判断是否包含 </think> 标签，还是得console看，要不真他妈费劲，艹！！！！！！！！！！！！！！！！！
                        // 检查是否包含分隔符
                        if (content.includes('</think>') || finalResponse.includes('</think>')) {
                          hasThinkTag = true;
                          thinkParts = finalResponse.split('</think>');

                          // 更新思考过程显示
                          this.chatHistory[this.chatHistory.length - 1].content = thinkParts[0].trim();
                        } else {
                          // 没有分隔符时，所有内容都是思考过程
                          currentThinkingText += content;
                          this.chatHistory[this.chatHistory.length - 1].content = currentThinkingText;
                        }
                      }
                    } catch (e) {
                      console.error('解析流数据失败:', e);
                    }
                  }
                }
              }
            } catch (error) {
              reject(error);
            }
          };

          processStream();
        });
      } catch (error) {
        console.error('调用 AI 接口失败:', error);
        throw error;
      }
    },

    scrollToBottom() {
      if (this.$refs.chatEnd) {
        this.$refs.chatEnd.scrollIntoView({ behavior: 'smooth' });
      }
    },
    copyMessage(text) {
      // 创建一个临时textarea元素
      const textarea = document.createElement('textarea');
      // 移除HTML标签和转义字符，保留换行
      const plainText = text
        .replace(/<br\s*\/?>/g, '\n')  // 将<br>转换为换行符
        .replace(/<[^>]+>/g, '')       // 移除其他HTML标签
        .replace(/&nbsp;/g, ' ')       // 转换空格
        .replace(/&lt;/g, '<')         // 转换<
        .replace(/&gt;/g, '>')         // 转换>
        .replace(/&amp;/g, '&')        // 转换&
        .replace(/&quot;/g, '"')       // 转换"
        .replace(/&#039;/g, "'");      // 转换'

      textarea.value = plainText;
      textarea.style.position = 'fixed';
      textarea.style.opacity = '0';
      document.body.appendChild(textarea);
      textarea.select();

      try {
        document.execCommand('copy');
        this.$message({
          message: '复制成功',
          type: 'success',
          duration: 2000
        });
      } catch (err) {
        this.$message.error('复制失败，请手动复制');
        console.error('复制失败:', err);
      } finally {
        document.body.removeChild(textarea);
      }
    }
  }
}
</script>

<style lang="scss" scoped>
@import '../../src/styles/index.scss';

/* 添加以下样式来确保表格正确显示 */
:deep(.markdown-table) {
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

:deep(.markdown-table th),
:deep(.markdown-table td) {
  padding: 12px 16px;
  text-align: left;
  border: 1px solid #e8e8e8;
}

:deep(.markdown-table th) {
  background-color: #f8fafd;
  font-weight: 600;
  color: #1f1f1f;
  border-bottom: 2px solid #1890ff;
}

:deep(.markdown-table tr:nth-child(even) td) {
  background-color: #fafafa;
}

:deep(.markdown-table tr:hover td) {
  background-color: #f0f7ff;
}

/* 代码块样式 */
:deep(.code-block) {
  background-color: #282c34;
  border-radius: 8px;
  margin: 12px 0;
  position: relative;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

:deep(.code-header) {
  background-color: #21252b;
  color: #abb2bf;
  font-size: 0.85em;
  padding: 6px 16px;
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  border-bottom: 1px solid #181a1f;
  display: flex;
  justify-content: space-between;
}

:deep(.code-block pre) {
  padding: 16px;
  margin: 0;
  overflow-x: auto;
}

:deep(.code-block code) {
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: 14px;
  line-height: 1.5;
  color: #abb2bf;
}

/* 行内代码样式 */
:deep(code:not(pre code)) {
  background-color: rgba(40, 44, 52, 0.05);
  padding: 2px 5px;
  border-radius: 4px;
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: 0.9em;
  color: #e06c75;
  border: 1px solid rgba(0, 0, 0, 0.1);
}

/* 高亮.js 语法高亮颜色 */
:deep(.hljs-keyword) {
  color: #c678dd;
}

:deep(.hljs-string) {
  color: #98c379;
}

:deep(.hljs-comment) {
  color: #5c6370;
  font-style: italic;
}

:deep(.hljs-function) {
  color: #61afef;
}

:deep(.hljs-number) {
  color: #d19a66;
}

:deep(.hljs-operator) {
  color: #56b6c2;
}

:deep(.hljs-class) {
  color: #e5c07b;
}

:deep(.hljs-title) {
  color: #61afef;
}

:deep(.hljs-params) {
  color: #d19a66;
}

:deep(.hljs-built_in) {
  color: #e06c75;
}

/* 添加以下样式来确保表格正确显示 */
:deep(table) {
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
  background-color: #ffffff;
}

:deep(th),
:deep(td) {
  padding: 12px 16px;
  text-align: left;
  border: 1px solid #e8e8e8;
  transition: all 0.3s ease;
}

:deep(th) {
  background: linear-gradient(to bottom, #f8fafd, #f0f5ff);
  font-weight: 600;
  color: #1f1f1f;
  border-bottom: 2px solid #1890ff;
  text-shadow: 0 1px 0 #fff;
  position: relative;
}

:deep(tr:nth-child(even) td) {
  background-color: #fafafa;
}

:deep(tr:hover td) {
  background-color: #f0f7ff;
}

/* 保留现有的 markdown-table 样式，但确保通用表格样式也能应用 */
:deep(.markdown-table) {
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

:deep(.markdown-table th),
:deep(.markdown-table td) {
  padding: 12px 16px;
  text-align: left;
  border: 1px solid #e8e8e8;
}

:deep(.markdown-table th) {
  background-color: #f8fafd;
  font-weight: 600;
  color: #1f1f1f;
  border-bottom: 2px solid #1890ff;
}

:deep(.markdown-table tr:nth-child(even) td) {
  background-color: #fafafa;
}

:deep(.markdown-table tr:hover td) {
  background-color: #f0f7ff;
}

/* 代码块样式保持不变 */
:deep(.code-block) {
  background-color: #282c34;
  border-radius: 8px;
  margin: 12px 0;
  position: relative;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
</style>